﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading;

namespace EBA.Framework
{
    /// <summary>
    /// 线程插件启动器。在一个子线程中启动对应的插件。
    /// </summary>
    [Serializable]
    public class RemotingThreadBundleLauncher<TBundle> : IBundleLauncher where TBundle : IBundle
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public RemotingThreadBundleLauncher()
        {
            var configFile = new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
            this.ConfigurationFileName = configFile.Name;
        }

        public string ConfigurationFileName { get; set; }

        /// <summary>
        /// 启动指定插件。
        /// </summary>
        /// <param name="runtime">插件运行时标识</param>
        /// <param name="startupPath">插件目录</param>
        /// <param name="bundleId">插件标识</param>
        public void Run(string runtime, string startupPath, string bundleId)
        {
            var appName = string.Format("laucherDomain-{0}", Guid.NewGuid());
            Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
            //AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;

            AppDomainSetup setup = new AppDomainSetup()
            {
                //http://blog.163.com/zjf_ha/blog/static/16179109320103751231221/
                ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,//保证共同使用的.Net类库的类型系统一致，否则Container比较Type的时候会有不同的HashCode
                PrivateBinPath = startupPath,
                ConfigurationFile = Path.Combine(startupPath, this.ConfigurationFileName),
                ApplicationName = appName,
                ShadowCopyFiles = "true",
                LoaderOptimization = LoaderOptimization.SingleDomain
            };

            this.LaucherDomain = AppDomain.CreateDomain(appName,
                evidence, setup);

            //this.LaucherDomain.AssemblyLoad += LaucherDomain_AssemblyLoad;
            //this.LaucherDomain.AssemblyResolve += LaucherDomain_AssemblyResolve;

            var remotingThreadType = typeof(RemotingBundleFactory);


            //this.LaucherDomain.Load(remotingThreadType.Assembly.FullName);


            var remotingThread = (RemotingBundleFactory)this.LaucherDomain.CreateInstanceFromAndUnwrap(remotingThreadType.Assembly.Location,
                 remotingThreadType.FullName);

            remotingThread.StartupPath = startupPath;
            this.RemotingBundle = remotingThread.CreateRemotingBundle<TBundle>(runtime, bundleId);

            // var bundleType = typeof(TBundle);

            //remotingThread.Initialize(runtime, bundleId, bundleType.AssemblyQualifiedName, bundleType.Assembly.FullName);
        }

        //Assembly LaucherDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        //{
        //    return null;
        //    //throw new NotImplementedException();
        //}

        //void LaucherDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
        //{
        //    var s = "";
        //    //throw new NotImplementedException();
        //}


        IDisposable RemotingBundle { get; set; }
        AppDomain LaucherDomain { get; set; }

        #region Dispose
        private bool _Disposed = false;
        /// <summary>
        /// 析构函数，释放资源。
        /// </summary>
        ~RemotingThreadBundleLauncher()
        {
            Dispose(false);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }


        /// <summary>
        /// 手动释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_Disposed)
            {
                if (disposing)
                {
                    if (this.RemotingBundle != null)
                    {
                        this.RemotingBundle.Dispose();
                    }

                    if (this.LaucherDomain != null)
                    {
                        AppDomain.Unload(this.LaucherDomain);
                        this.LaucherDomain = null;
                    }

                    GC.SuppressFinalize(this);
                }
                _Disposed = true;
            }
        }
        #endregion
    }
}
